/**
 *	\file AKTTTStateMaintainer.m
 *  
 *  \section desc_sec Description
 *  Description
 *  
 *  \if copyright
 *  	Copyright 2008 Ben Chen. All rights reserved.
 *  \endif
 *  
 *  \section hist_sec Change History:
 *  	-	1/16/08		BC		Created this file
 *  
 *  <hr>
 */

#import "AKTTTStateMaintainer.h"

#define KeyState			@"state"
#define KeyNextPlayerSide	@"nextPlayerSide"

// Keys for archiving
NSString * const kAKTTTSMPieces			= @"AKTTTSMPiecesKey"; 
NSString * const kAKTTTSMNextPlayerSide	= @"AKTTTSMNextPlayerKey";

@interface AKTTTStateMaintainer ()
@property (readwrite, copy) NSArray *eliminatedPieces, *createdPieces, *movedPieces, *winners;
@property (readwrite, assign) AKSideIndex nextPlayerSide;
@end

@implementation AKTTTStateMaintainer

@synthesize eliminatedPieces, createdPieces, movedPieces, winners, nextPlayerSide;

#pragma mark Archiving

- (void)setStateWithPieceDict:(NSDictionary*)aDic nextPlayerSide:(AKSideIndex)side {
	NSMutableArray *tempArray = [NSMutableArray array];
	for (NSString *aKey in aDic) {	
		NSValue *pieceValue = [aDic valueForKey:aKey];				
		AKPiece thePiece;
		[pieceValue getValue:&thePiece];
		pieces[thePiece.cell] = thePiece;
		[tempArray addObject:pieceValue];
	}
	
	allPieces				= [aDic mutableCopy];
	self.createdPieces		= [tempArray copy];
	self.eliminatedPieces	= nil;
	self.movedPieces		= nil;
	
	self.nextPlayerSide	= side;
	self.winners		= nil;
}

- (id)initWithCoder:(NSCoder*)coder {
	[super init];
	[self setStateWithPieceDict:[coder decodeObjectForKey:kAKTTTSMPieces] 
				 nextPlayerSide:[coder decodeIntegerForKey:kAKTTTSMNextPlayerSide]];
	return self;
}

- (void)encodeWithCoder:(NSCoder*)coder {
	[coder encodeObject:[allPieces copy] forKey:kAKTTTSMPieces];
	[coder encodeInteger:nextPlayerSide forKey:kAKTTTSMNextPlayerSide];
}

#pragma mark Initialization

- (id)init {
	self = [super init];
	if (self != nil) {
		[self backToInitialState];
	}
	return self;
}

- (void)dealloc {
	[allPieces release];
	[super dealloc];
}

#pragma mark All Pieces

- (NSDictionary*)allPieces {
	return [allPieces copy];
}

#pragma mark Helper Function

BOOL isSameInLine(unsigned start, unsigned end, unsigned inter, AKPiece pieces[]) {
	BOOL retVal = NO;
	
	if (pieces[start].side != NSNotFound) {
		unsigned i;
		for (i = start+inter; i <= end; i+= inter) {
			if (pieces[i].side != pieces[start].side)
				break;
		}
		if (i > end)
			retVal = YES;
	}
	
	return retVal;
}

- (AKSideIndex)findWinner {
	AKSideIndex retVal = NSNotFound;
	
	// 0 1 2
	// 3 4 5
	// 6 7 8
	
	// Three in a row
	if (isSameInLine(0, 2, 1, pieces))
		retVal = pieces[0].side;
	else if (isSameInLine(3, 5, 1, pieces))
		retVal = pieces[3].side;
	else if (isSameInLine(6, 8, 1, pieces))
		retVal = pieces[6].side;
	// Three in a column
	else if (isSameInLine(0, 6, 3, pieces))
		retVal = pieces[0].side;
	else if (isSameInLine(1, 7, 3, pieces))
		retVal = pieces[1].side;
	else if (isSameInLine(2, 8, 3, pieces))
		retVal = pieces[2].side;
	// Three in a diagnal
	else if (isSameInLine(0, 8, 4, pieces))
		retVal = pieces[0].side;
	else if (isSameInLine(2, 6, 2, pieces))
		retVal = pieces[2].side;
	
	return retVal;
}		

- (BOOL)boardIsFull {
	BOOL retVal = NO;
	
	int i;
	for (i = 0; i < AKTTTNumOfCells; ++i) {
		if (pieces[i].piece == NSNotFound)
			break;
	}
	
	if (i == AKTTTNumOfCells)
		retVal = YES;
	
	return retVal;
}

/**
 *  Go back to initial state.
 */
- (void)backToInitialState {
	if (allPieces == nil)
		allPieces = [[NSMutableDictionary alloc] init];
	
	int i;
	for (i = 0; i < AKTTTNumOfCells; ++i) {		
		pieces[i].cell	= i;
		pieces[i].piece	= NSNotFound;
		pieces[i].side	= NSNotFound;
	}
	
	[allPieces	removeAllObjects];
	self.createdPieces		= nil;
	self.eliminatedPieces	= nil;
	self.movedPieces		= nil;
	
	self.nextPlayerSide	= AKTTTBlack;	// black goes first
	self.winners		= nil;
}

/**
 *  Perform a move.
 *  \param[in] newMove A new move.
 *  \return YES if the move has been made, otherwise NO meaning the move is not permitted.
 */
- (BOOL)performMove:(AKMove)newMove {
	BOOL retVal;
	
	if ([winners count] > 0 || newMove.dstCell > AKTTTNumOfCells || newMove.side != nextPlayerSide || newMove.piece > 0) {
		retVal = NO;
	} else {
		AKPiece *pieceInfo = &pieces[newMove.dstCell];
		
		if (pieceInfo->side == newMove.side) {
			// The destination cell must be either empty or has the opponent's piece
			retVal = NO;
		} else {
			if (pieceInfo->piece != NSNotFound)
				self.eliminatedPieces = [NSArray arrayWithObject:[NSValue valueWithBytes:pieceInfo objCType:@encode(AKPiece)]];
			else
				self.eliminatedPieces = nil;
			
			pieceInfo->cell  = newMove.dstCell;
			pieceInfo->piece = newMove.piece;
			pieceInfo->side  = newMove.side;
			
			if (newMove.srcCell != NSNotFound) {
				// We allow piece to move in Tic Tac Toe
				AKPiece *oldPiece = &pieces[newMove.srcCell];
				oldPiece->piece = NSNotFound;
				oldPiece->side	= NSNotFound;
				[allPieces setValue:[NSValue valueWithBytes:&AKZeroPiece objCType:@encode(AKPiece)] 
							 forKey:AKStringFromCellIndex(newMove.srcCell)];
				[allPieces setValue:[NSValue valueWithBytes:&pieces[newMove.dstCell] objCType:@encode(AKPiece)] 
							 forKey:AKStringFromCellIndex(newMove.dstCell)];
				self.createdPieces = nil;
				self.movedPieces = [NSArray arrayWithObject:[NSValue valueWithBytes:&newMove objCType:@encode(AKMove)]];
			} else {
				[allPieces setValue:[NSValue valueWithBytes:&pieces[newMove.dstCell] objCType:@encode(AKPiece)] 
							 forKey:AKStringFromCellIndex(newMove.dstCell)];
				self.movedPieces = nil;
				self.createdPieces = [NSArray arrayWithObject:[NSValue valueWithBytes:pieceInfo objCType:@encode(AKPiece)]];
			}
						
			// Find winner
			AKSideIndex newWinner = [self findWinner];
			if (newWinner != NSNotFound) {
				self.winners = [NSArray arrayWithObject:[NSNumber numberWithUnsignedInt:newWinner]];
				self.nextPlayerSide = NSNotFound;
			} else if ([self boardIsFull]) {
				self.winners = [NSArray arrayWithObjects:[NSNumber numberWithUnsignedInt:0], [NSNumber numberWithUnsignedInt:1], nil];
				self.nextPlayerSide = NSNotFound;
			} else
				// Change side
				self.nextPlayerSide = (nextPlayerSide == AKTTTBlack) ? AKTTTWhite : AKTTTBlack;
			
			retVal = YES;
		}
	}
			
	return retVal;
}

- (NSArray*)reachableCellsForPiece:(AKPiece)piece {
	NSArray *retVal = nil;
	
	// For the sack of testing, we allow piece to move around and capture the opponent's piece
	if (piece.side == self.nextPlayerSide) {
		NSMutableArray *tempArray = [NSMutableArray array];
		unsigned i;
		for (i = 0; i < AKTTTNumOfCells; ++i) {
			if (pieces[i].piece == NSNotFound || (pieces[i].side != NSNotFound && pieces[i].side != piece.side)) {
				[tempArray addObject:[NSNumber numberWithUnsignedInt:i]];
			}
		}
		
		if ([tempArray count] > 0) {
			retVal = [[tempArray copy] autorelease];
		}
	}
	
	return retVal;
}

- (NSArray*)newPiecesAtCell:(AKCellIndex)cell {
	NSArray *retVal = nil;
	
	// Validate the cell index
	if (cell < AKTTTNumOfCells && pieces[cell].piece == NSNotFound) {
		retVal = [NSArray arrayWithObject:[NSNumber numberWithUnsignedInt:0]];
	}

	return retVal;
}

- (AKPiece)moveablePieceAtCell:(AKCellIndex)cell {
	AKPiece retVal = AKZeroPiece;
	
	if (cell >= 0 && cell <= 9 && pieces[cell].piece != NSNotFound && pieces[cell].side == self.nextPlayerSide) {
		retVal = pieces[cell];
	}
	
	return retVal;
}

@end
